<?php

abstract class AbstractService
{

    protected $_mapper;

    public function __construct()
    {
	$this->_init();
    }

    public function __call($name, $arguments)
    {
	return call_user_func_array(array($this->getMapper(), $name), $arguments);
    }

    abstract protected function _init();

    public function setMapper(AbstractMapper $mapper)
    {
	$this->_mapper = $mapper;
    }

    public function getMapper()
    {
	return $this->_mapper;
    }

    public function getCacheSlotClass($type)
    {
	$cacheSlotClass = 'CacheSlots' . ucfirst($this->getMapper()->getTableName()) . ucfirst($type);
	if (!class_exists($cacheSlotClass)) {
	    throw new Exception("Cache slot class is not defined");
	}
	return $cacheSlotClass;
    }

    public function getCount($params = null)
    {
	$cacheSlotClass = $this->getCacheSlotClass('count');
	$cacheSlot = new $cacheSlotClass($params);
	if (($count = $cacheSlot->load()) === false) {
	    $count = $this->getMapper()->getCount($params);
	    $cacheSlot->setData($count)->save();
	}
	return $count;
    }

    public function getList($params = null, $dependency = null)
    {
	$cacheSlotClass = $this->getCacheSlotClass('list');
	$cacheSlot = new $cacheSlotClass($params, Yii::app()->params['cache']['lifetime'], $dependency);
	if (($data = $cacheSlot->load()) === false) {
	    $data = $this->getMapper()->getAll($params);
	    $cacheSlot->setData($data)->save();
	}
	return $data;
    }

    public function save(AbstractModel $model)
    {
	if (!$this->onBeforeSave($model)) {
	    return false;
	}
	$data = (array) $model->getDbData() + $model->getSqlData();
	$result = false;
	if (!$model->getId()) {
	    $result = $this->getMapper()->insert($data);
	    if ($result) {
		$model->setId($result);
	    }
	}
	else {
	    $result = $this->getMapper()->update($data, 'id = :id', array(':id' => $model->getId()));
	}

	if (!$this->onAfterSave($model)) {
	    return false;
	}
	return $result;
    }

    public function remove(AbstractModel $model)
    {
	if (!$this->onBeforeRemove($model)) {
	    return false;
	}
	if (!$model->getId()) {
	    return false;
	}
	if ($this->getMapper()->delete('id = :id', array(':id' => $model->getId()))) {
	    $cacheSlot = new CacheSlotsPk($this->getMapper()->getTableName(), $model->getId());
	    $cacheSlot->delete();
	    return true;
	}
	if (!$this->onAfterRemove($model)) {
	    return false;
	}
	return false;
    }

    public function onBeforeRemove(AbstractModel $model)
    {
	return true;
    }

    public function onAfterRemove(AbstractModel $model)
    {
	return true;
    }

    public function onBeforeSave(AbstractModel $model = null)
    {
	return true;
    }

    public function onAfterSave(AbstractModel $model = null)
    {
	return true;
    }

    public function get($id, $cache = true)
    {
	if (!$id) {
	    return false;
	}

	$tableName = $this->getMapper()->getTableName();

	$cacheSlot = new CacheSlotsPk($tableName, $id);

	if (($data = $cacheSlot->load()) === false) {
	    $data = $this->getMapper()->getById($id);
	    if ($cache) {
		$cacheSlot->setData($data)->save();
	    }
	}

	return $data;
    }

    /**
     * compare review model features such as image, video, map
     * it means to be: array('tableName' => array('idKey', 'idKey')).
     * @param array $features
     */
    public function compareFeatures(AbstractModel $model, array $features)
    {
	if (!$model->getId() || empty($features)) {
	    return false;
	}

	foreach ($features as $service => $data) {
	    $service = FactoryService::get($service);
	    if (!$service) {
		continue;
	    }
	    if (is_array($data)) {
		foreach ($data as $modelId) {
		    $serviceModel = $service->get($modelId);
		    if ($serviceModel) {
			$this->getMapper()->makeLink(array(
			    $this->getMapper()->getTableName() => $model->getId()
				), array(
			    $service->getMapper()->getTableName() => $serviceModel->getId()
			));
		    }
		}
	    }
	}
    }

}
